#pragma once

#include "./basic_property_server.hpp"
#include "modpash_include_config.hpp"


namespace modpash {

    template <typename TransceiverType, uint16_t REGISTER_COUNT, uint8_t EXTRA_INFO_COUNT = 0>
    class BasicArrayServer : public BasicPropertyServer<TransceiverType, EXTRA_INFO_COUNT> {
        // 16 指令的请求帧是支持的数据帧中长度最大的，且与寄存器数量有关，
        // 如果主机要一次写入所有寄存器，则对应的16 请求帧必须小于等于最大可接受的帧长度。
        static_assert((REGISTER_COUNT * 2 + 5) <= MAX_RX_ADU_SIZE);

       private:
        uint16_t _reg[REGISTER_COUNT];         // 寄存器数组
        uint16_t _reg_buffer[REGISTER_COUNT];  // 在写入寄存器前存储临时值
        uint16_t _reg_write_start = 0;         // 前一次写入寄存器的起始地址
        uint16_t _reg_write_end = 0;           // 前一次写入寄存器的最后一个地址 + 1，所以最少等于1

       protected:
        /**
         * @brief 接收到03 指令后调用，准备寄存器数据，或执行参数检查。
         *
         * 返回RESPONSE_TYPE_OK，正常响应，否则不响应，或返回异常响应。
         *
         * @param address
         * @param count
         * @return int_fast8_t
         */
        virtual int_fast8_t before_read_register(uint16_t address, uint8_t count) = 0;


        /**
         * @brief 接收06 或16 指令后调用，在写入寄存器之前先验证，只要有一个返回异常，就取消所有写入。
         *
         * 可以调用value_from_register_buffer 和value32_from_register_buffer 获取本次要写入的其他寄存器值。
         *
         * 返回RESPONSE_TYPE_OK，验证OK，将值写入寄存器；
         * 返回RESPONSE_TYPE_IGNORE，忽略写入的值，保持原值；
         * 返回RESPONSE_TYPE_NO_RESPONSE，终止响应，取消所有写入；
         * 所有异常码都取消写入，并返回异常响应；
         *
         * 如果寄存器当前是只读的，可以返回RESPONSE_TYPE_IGNORE。
         * 写入只读寄存器如果产生异常响应，会导致一次写入多个寄存器的操作失败，
         * 这可能不是个好的行为。比如，要是一堆寄存器中间有一个只读的，
         * 那要一次写入所有寄存器的话，必须分成两步，先写前半部分，再写后半部分，
         * 不然就会异常。所以只读寄存器应该忽略写入的值，不返回异常响应。
         *
         * @param address
         * @param value
         * @return int_fast8_t
         */
        virtual int_fast8_t before_write_register(uint16_t address, uint16_t value) = 0;


        /**
         * @brief 在返回OK 响应并写入寄存器之后调用，执行对应寄存器写入后的操作。
         *
         * 执行过程中，Modbus 服务器无法响应新的请求。
         *
         * @param address
         * @param value
         * @param last_value
         */
        virtual void after_write_register(uint16_t address, uint16_t value, uint16_t last_value) = 0;


        /**
         * @brief 在寄存器数据被主机写入并更改后调用，主要用来把寄存器值保存到flash
         *
         * 执行过程中，Modbus 服务器无法响应新的请求。
         *
         * @param address
         * @param value
         * @param last_value
         */
        virtual void after_change_register() {}


        bool check_register_count(uint16_t address, uint8_t count) {
            return (address + count) >= REGISTER_COUNT;
        }


        uint16_t value_from_register_buffer(uint16_t address) const {
            return _reg_buffer[address];
        }


        uint16_t value32_from_register_buffer(uint16_t address_high) const {
            auto h = _reg_buffer[address_high];
            auto l = _reg_buffer[address_high + 1];
            return (static_cast<uint16_t>(h) << 8) + l;
        }


        virtual int_fast8_t on_read(uint16_t address, uint8_t count) override {
            if (check_register_count(address, count)) {
                return modpash::RESPONSE_ERROR_INVALID_COUNT;
            }

            // 调用before_read，准备数据，或执行其他检查
            // 如果返回OK 以外的值，就不发送响应
            auto r = before_read_register(address, count);

            if (r == modpash::RESPONSE_TYPE_OK) {
                // 异常响应必须在begin_response 之前全部处理掉，
                // 否则会导致帧结构错误
                if (this->is_response_allowed()) {
                    this->begin_response();

                    auto end = address + count;
                    for (; address < end; ++address) {
                        this->send_data(_reg[address]);
                    }

                    this->end_response();
                }
            }

            return r;
        }


        virtual int_fast8_t on_write(uint16_t address, uint8_t count) override {
            if (check_register_count(address, count)) {
                return modpash::RESPONSE_ERROR_INVALID_COUNT;
            }

            // 先把寄存器值拷贝到临时数组里，验证完后再写入寄存器
            memcpy(_reg_buffer, _reg, sizeof(_reg));

            _reg_write_start = address;
            _reg_write_end = address + count;
            int_fast8_t r = RESPONSE_TYPE_NONE;

            // 先把数据写入临时数组里，同时一个一个验证，
            // 只要有一个错误，就丢弃所有要写入的值，并返回异常码
            for (; address < _reg_write_end; ++address) {
                uint16_t v = this->template read_property<uint16_t>();
                r = before_write_register(address, v);

                switch (r) {
                    case RESPONSE_TYPE_OK:
                        _reg_buffer[address] = v;
                        break;

                    case RESPONSE_TYPE_IGNORE:
                        // 忽略写入值，仍然返回OK
                        break;

                    default:
                        // SILENT 、NONE 或异常码，中止验证
                        return r;
                }
            }

            return RESPONSE_TYPE_OK;
        }


        virtual void after_write() override {
            // if (_reg_write_end > _reg_write_start) {
            uint16_t address = _reg_write_start;
            uint16_t end = _reg_write_end;
            bool changed = false;
            
            for (; address < end; ++address) {
                uint16_t last_value = value(address);
                uint16_t value = value_from_register_buffer(address);
                
                if (value != last_value) {
                    set_value(address, value);
                    changed = true;
                }

                after_write_register(address, value, last_value);
            }

            if (changed) {
                after_change_register();
            }
            // }
        }


       public:
        BasicArrayServer(TransceiverType& rtx) noexcept :
            BasicPropertyServer<TransceiverType, EXTRA_INFO_COUNT>(rtx, REGISTER_COUNT - 1) {}


        uint16_t value(uint16_t index) const {
            return _reg[index];
        }


        uint32_t value32(uint16_t index_high) const {
            if (index_high >= REGISTER_COUNT) {
                return 0;
            }

            return (static_cast<uint32_t>(_reg[index_high]) << 16) | _reg[index_high + 1];
        }


        void set_value(uint16_t index, uint16_t v) {
            _reg[index] = v;
        }


        void set_value32(uint16_t index_high, uint32_t v) {
            if (index_high >= REGISTER_COUNT) {
                return;
            }

            _reg[index_high] = v >> 16;
            _reg[index_high + 1] = v & 0xFFFF;
        }


        const uint16_t* register_array() const {
            return _reg;
        }


        uint16_t* register_array() {
            return _reg;
        }


        void init_register(uint16_t init_value) {
            for (auto& v : _reg) {
                v = init_value;
            }
        }
    };
}  // namespace modpash